package com.huitong.common.redis.manager;

import com.huitong.common.redis.utils.RedisUtils;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.redisson.api.RMap;
import org.redisson.api.RMapCache;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonCache;
import org.springframework.boot.convert.DurationStyle;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 自定义缓存管理器
 *
 * @author fromdrowning
 * @date 2024/3/16 22:55
 */
@NoArgsConstructor
public class CustomCacheManager implements CacheManager {

    /**
     * 缓存配置
     */
    private final Map<String, CacheConfig> configMap = new ConcurrentHashMap<>();

    /**
     * 缓存实例
     */
    private final ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();

    /**
     * 是否动态 默认{@code true}
     */
    @Setter
    private boolean dynamic = true;

    /**
     * 是否允许空值 默认{@code true}
     */
    @Setter
    private boolean allowNullValues = true;

    /**
     * 是否事务感知 默认{@code true}
     */
    @Setter
    private boolean transactionAware = true;

    /**
     * 获取缓存实例
     *
     * @param name 缓存名称
     * @return 缓存实例
     */
    @Override
    public Cache getCache(@Nullable String name) {
        // 重写cacheName支持多参数
        String[] array = StringUtils.delimitedListToStringArray(name, "#");
        name = array[0];

        Cache cache = instanceMap.get(name);
        if (cache != null || !dynamic) {
            return cache;
        }

        CacheConfig config = configMap.computeIfAbsent(name, k -> new CacheConfig());

        if (array.length > 3) {
            config.setMaxSize(Integer.parseInt(array[3]));
        } else if (array.length > 2) {
            config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis());
        } else if (array.length > 1) {
            config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis());
        }

        if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) {
            return createMap(name);
        }

        return createMapCache(name, config);
    }

    /**
     * 创建Map缓存
     *
     * @param name 缓存名称
     * @return 缓存实例
     */
    private Cache createMap(String name) {
        RMap<Object, Object> map = RedisUtils.getRedissonClient().getMap(name);

        Cache cache = new RedissonCache(map, allowNullValues);
        if (transactionAware) {
            cache = new TransactionAwareCacheDecorator(cache);
        }

        Cache oldCache = instanceMap.putIfAbsent(name, cache);
        if (oldCache != null) {
            cache = oldCache;
        }

        return cache;
    }

    /**
     * 创建Map缓存
     *
     * @param name   缓存名称
     * @param config 缓存配置
     * @return 缓存实例
     */
    private Cache createMapCache(String name, CacheConfig config) {
        RMapCache<Object, Object> map = RedisUtils.getRedissonClient().getMapCache(name);

        Cache cache = new RedissonCache(map, config, allowNullValues);
        if (transactionAware) {
            cache = new TransactionAwareCacheDecorator(cache);
        }

        Cache oldCache = instanceMap.putIfAbsent(name, cache);
        if (oldCache != null) {
            cache = oldCache;
        } else {
            map.setMaxSize(config.getMaxSize());
        }

        return cache;
    }

    /**
     * 获取缓存名称集合
     *
     * @return 缓存名称集合
     */
    @Override
    public Collection<String> getCacheNames() {
        return Collections.unmodifiableSet(configMap.keySet());
    }
}
